package com.sec.android.app.myfiles.decorator.gridview;

import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Point;
import android.util.LruCache;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.decorator.AbsAdapterImpDecorator;
import com.sec.android.app.myfiles.fragment.filelist.FileListAdapter;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.info.MediaFile;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.local.category.CategoryFileRecord;
import com.sec.android.app.myfiles.operation.FileOperator;
import com.sec.android.app.myfiles.provider.DbTableInfo;
import com.sec.android.app.myfiles.util.FileDetailUtils;
import com.sec.android.app.myfiles.util.UiUtils;
import com.sec.android.app.myfiles.widget.listview.FileListViewHolder;
import com.sec.android.app.myfiles.widget.listview.ListViewHolder;
import com.sec.android.app.myfiles.widget.listview.pinchview.PinchGridView;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by jaebae on 2016-04-18.
 */
public class GridViewDecorator extends AbsAdapterImpDecorator {
    private int mWidth[], mHeight[];
    private int mFileTypeIconWidth[];
    private int mFileTypeIconHeight[];
    private int mCreateFolderIconSize[];
    private int mDepth;
    private int mDetailWeight[];
    private SparseBooleanArray mSetDetailWeight;
    private final DbTableInfo mTableInfo;

    private final BlockingQueue<Runnable> mLoadQueue = new LinkedBlockingQueue<>();
    private final ThreadPoolExecutor mLoadThreadPool = new ThreadPoolExecutor(
            AppConstants.NUMBER_OF_THREADS, AppConstants.NUMBER_OF_THREADS,
            AppConstants.IDLE_THREAD_KEEP_ALIVE_TIME, AppConstants.IDLE_THREAD_KEEP_ALIVE_TIME_UNIT,
            mLoadQueue);
    private final FileRecord mCurRecord;

    private static final String UNKNOWN = "unknown";

    public GridViewDecorator(Context context, FileListAdapter adapter) {
        super(context, adapter);
        setDefaultLayoutValue(context);
        if (mAdapterImp == null) {
            mTableInfo = null;
            mCurRecord = null;
        } else {
            mTableInfo = mAdapterImp.getTableInfo();
            mCurRecord = mAdapterImp.getCurRecord();
        }
    }

    private void setDefaultLayoutValue(Context context) {
        Resources res = context.getResources();
        mWidth = new int[]{
                (int) res.getDimension(R.dimen.category_grid_two_item_size),
                (int) res.getDimension(R.dimen.category_grid_three_item_size),
                (int) res.getDimension(R.dimen.list_item_grid_size)
        };
        mFileTypeIconWidth = new int[]{
                (int) res.getDimension(R.dimen.category_grid_two_item_file_type_icon_size_width),
                (int) res.getDimension(R.dimen.category_grid_three_item_file_type_icon_size_width),
                (int) res.getDimension(R.dimen.category_grid_four_item_file_type_icon_size_width)
        };
        mFileTypeIconHeight = new int[]{
                (int) res.getDimension(R.dimen.category_grid_two_item_file_type_icon_size),
                (int) res.getDimension(R.dimen.category_grid_three_item_file_type_icon_size),
                (int) res.getDimension(R.dimen.category_grid_four_item_file_type_icon_size)
        };
        mCreateFolderIconSize = new int[]{
                (int) res.getDimension(R.dimen.category_grid_two_item_create_folder_icon_size),
                (int) res.getDimension(R.dimen.category_grid_three_item_create_folder_icon_size),
                (int) res.getDimension(R.dimen.create_folder_icon_size)
        };

        mHeight = new int[3];
        mDetailWeight = new int[3];
        mSetDetailWeight = new SparseBooleanArray();
    }

    @Override
    protected void _createViewHolder(View v, ListViewHolder vh) {
        vh.addView(FileListViewHolder.VIDEO_PLAYTIME_PROGRESSBAR, v.findViewById(R.id.video_playtime_progressbar));
        vh.addView(FileListViewHolder.LAYOUT_FILE_DETAIL, v.findViewById(R.id.layout_file_detail));
        vh.addView(FileListViewHolder.LAYOUT_THUMBNAIL, v.findViewById(R.id.layout_thumbnail));
    }

    @Override
    protected void _bindView(Context context, ListViewHolder vh, View v, FileRecord record, int position) {
        setScaleView(vh, v);
        _bindCategoryDetail(vh, record);
    }

    @Override
    protected void _bindProgressSync(ListViewHolder vh, View v, FileRecord record, boolean isShow) {
        AbsListView listView = mAdapterImp.getListView().getListView();
        if (listView instanceof PinchGridView) {
            Resources res = mContext.getResources();
            mDepth = ((PinchGridView) listView).getPinchDepth();
            int iconWidth = 0;
            String syncString = null;
            switch (mDepth) {
                case 2:
                    iconWidth = (int) res.getDimension(R.dimen.list_item_progress_sync_ic_width_small);
                    break;
                case 1:
                case 0:
                    syncString = res.getString(R.string.sync_text_with_dot);
                    iconWidth = (int) res.getDimension(R.dimen.list_item_progress_sync_ic_width);
                    break;
            }
            ProgressBar progressBar = vh.getView(FileListViewHolder.PROGRESS_SYNC, ProgressBar.class);
            if (progressBar != null) {
                ViewGroup.LayoutParams params = progressBar.getLayoutParams();
                params.width = iconWidth;
                params.height = iconWidth;
            }

            int padding = 0;
            if (isShow) {
                padding = mContext.getResources().getDimensionPixelSize(R.dimen.grid_item_progress_margin_end) + iconWidth;
            }
            TextView name = vh.getView(FileListViewHolder.NAME, TextView.class);
            if (name != null) {
                name.setPaddingRelative(name.getPaddingStart(), name.getPaddingTop(), padding, name.getPaddingBottom());
            }
            TextView size = vh.getView(FileListViewHolder.SIZE, TextView.class);
            if (size != null) {
                size.setAlpha(isShow ? 0.37f : 1f);
                if (record.getFileType() == FileType.FOLDER) {
                    size.setText(isShow ? syncString : UiUtils.makeItemsString(mContext, record.getItemCount(false)));
                }
            }
            TextView date = vh.getView(FileListViewHolder.DATE, TextView.class);
            if (date != null) {
                date.setAlpha(isShow ? 0.37f : 1f);
            }
        }
    }

    @Override
    protected void _bindCreateFolderView(ListViewHolder vh, View v) {
        ImageView createFolderIcon = vh.getView(FileListViewHolder.CREATE_FOLDER_ICON, ImageView.class);
        if (createFolderIcon != null) {
            ViewGroup.LayoutParams params = createFolderIcon.getLayoutParams();
            params.width = mCreateFolderIconSize[mDepth];
            params.height = mCreateFolderIconSize[mDepth];
            createFolderIcon.setLayoutParams(params);
        }

        TextView name = vh.getView(FileListViewHolder.NAME, TextView.class);
        if (name != null) {
            name.setPaddingRelative(0, 0, 0, 0);
        }
        setAlignment(vh, FileListViewHolder.NAME, TextView.TEXT_ALIGNMENT_CENTER);

        TextView description = vh.getView(FileListViewHolder.DESCRIPTION, TextView.class);
        if (description != null) {
            description.setVisibility(View.GONE);
        }
    }

    @Override
    protected boolean _getFileRecord(Cursor cursor, FileRecord record) {
        boolean bRet = true;
        if (record != null && record.getStorageType() != FileRecord.StorageType.Cloud && record.getDetailMediaInfo() == null) {
            if (record.isImageFileType()) {
                bRet = setImageInfo(record, cursor);
            } else if (record.isAudioFileType()) {
                bRet = setAudioInfo(record, cursor);
            } else if (record.isVideoFileType()) {
                bRet = setVideoInfo(record, cursor);
            }
        }
        return bRet;
    }


    @Override
    protected void updateRecordDetail(FileRecord record) {
        if (!FileOperator.isWorking()) {
            mLoadThreadPool.execute(new UpdateRecordRunnable(mContext, mTableInfo, record));
        }
    }

    private static class UpdateRecordRunnable implements Runnable {
        private final Context mContext;
        private final DbTableInfo mDbTableInfo;
        private final FileRecord mRecord;

        private UpdateRecordRunnable(Context context, DbTableInfo dbTableInfo, FileRecord record) {
            mContext = context;
            mDbTableInfo = dbTableInfo;
            mRecord = record;
        }

        @Override
        public void run() {
            if (mRecord.isImageFileType()) {
                if (mRecord.getDetailMediaInfo()[0] == null) {
                    FileDetailUtils.updateImageDetailInfo(mContext, mDbTableInfo, mRecord);
                }
            } else if (mRecord.isAudioFileType()) {
                int fileType = MediaFile.getFileType(mRecord.getFullPath(), false);
                if ((mRecord.getDetailMediaInfo()[0] == null || mRecord.getDetailMediaInfo()[1] == null) && !FileType.isDrmFileType(fileType)) {
                    FileDetailUtils.updateAudioDetailInfo(mContext, mDbTableInfo, mRecord);
                }
            } else if (mRecord.isVideoFileType()) {
                FileDetailUtils.updateVideoDetailInfo(mContext, mDbTableInfo, mRecord);
            }
        }
    }

    private void _bindCategoryDetail(ListViewHolder vh, FileRecord record) {
        ProgressBar progressBar = vh.getView(FileListViewHolder.VIDEO_PLAYTIME_PROGRESSBAR, ProgressBar.class);

        if (progressBar != null) {
            progressBar.setVisibility(View.GONE);
        }

        Object detailMediaInfo[] = record.getDetailMediaInfo();
        if (detailMediaInfo != null) {
            if (record.isImageFileType()) {
                bindImageDetail(vh, detailMediaInfo);
            } else if (record.isAudioFileType()) {
                bindAudioDetail(vh, detailMediaInfo);
            } else if (record.isVideoFileType()) {
                bindVideoDetail(vh, detailMediaInfo, progressBar);
            }
        }

    }

    private void bindImageDetail(ListViewHolder vh, Object detailMediaInfo[]) {
        TextView dateView = (TextView) vh.getView(FileListViewHolder.DATE);

        if (dateView != null && detailMediaInfo[0] != null) {
            dateView.setText((String) detailMediaInfo[0]);
        }
    }

    private void bindAudioDetail(ListViewHolder vh, Object detailMediaInfo[]) {
        TextView sizeView = (TextView) vh.getView(FileListViewHolder.SIZE);
        if (sizeView != null && detailMediaInfo[0] != null && !detailMediaInfo[0].equals(UNKNOWN)) {
            sizeView.setText((String) detailMediaInfo[0]);
        }
        TextView dateView = (TextView) vh.getView(FileListViewHolder.DATE);
        if (dateView != null && detailMediaInfo[1] != null && !detailMediaInfo[1].equals(UNKNOWN)) {
            dateView.setText((String) detailMediaInfo[1]);
        }
    }

    private void bindVideoDetail(ListViewHolder vh, Object detailMediaInfo[], ProgressBar progressBar) {
        TextView dateView = (TextView) vh.getView(FileListViewHolder.DATE);
        long duration = (long) detailMediaInfo[0];

        if (dateView != null) {
            dateView.setText(makeDurationString(duration));
        }

        bindProgressBar(progressBar, detailMediaInfo, duration);
    }

    private void bindProgressBar(ProgressBar progressBar, Object detailMediaInfo[], long duration) {
        if (progressBar != null && mDepth != 2 && isCategoryList()) {
            Long resumePos = (Long) detailMediaInfo[1];
            if (resumePos != null && duration != 0 && resumePos != 0 && duration != resumePos) {
                int progress = (int) (resumePos * 100 / duration);
                progressBar.setVisibility(View.VISIBLE);
                progressBar.setProgress(progress);
            }
        }
    }

    private boolean isCategoryList() {
        boolean bRet = false;
        if (mCurRecord != null) {
            if (mCurRecord instanceof CategoryFileRecord) {
                bRet = true;
            }
        }
        return bRet;
    }

    private static final LruCache<Long, String> sCachedDuration = new LruCache<>(128);

    private String makeDurationString(long time) {
        String ret = sCachedDuration.get(time);
        if (ret == null) {
            ret = UiUtils.makeDurationString(time);
            sCachedDuration.put(time, ret);
        }
        return ret;
    }

    private void setScaleView(ListViewHolder vh, View v) {
        AbsListView listView = mAdapterImp.getListView().getListView();
        if (listView instanceof PinchGridView) {
            mDepth = ((PinchGridView) listView).getPinchDepth();
            RelativeLayout layoutThumbnail = (RelativeLayout) vh.getView(FileListViewHolder.LAYOUT_THUMBNAIL);
            RelativeLayout layoutDetail = (RelativeLayout) vh.getView(FileListViewHolder.LAYOUT_FILE_DETAIL);

            setScaleViewSize(v);
            setFileTypeIconSize(vh);
            setTextSize(vh);

            int detailWeight = mDetailWeight[mDepth];
            int thumnailWeight = 1000 - detailWeight;

            setWeightLayout(layoutThumbnail, layoutDetail, thumnailWeight, detailWeight);
            switch (mDepth) {
                case 2:
                    setAlignment(vh, FileListViewHolder.NAME, TextView.TEXT_ALIGNMENT_CENTER);
                    break;
                case 1:
                case 0:
                    setAlignment(vh, FileListViewHolder.NAME, TextView.TEXT_ALIGNMENT_VIEW_START);
                    break;
            }
        }
    }

    private void setTextSize(ListViewHolder vh) {
        Resources res = mContext.getResources();

        TextView textView = (TextView) vh.getView(FileListViewHolder.NAME);
        TextView sizeView = (TextView) vh.getView(FileListViewHolder.SIZE);
        if (textView != null && sizeView != null) {
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
            lp.setMargins(0, mContext.getResources().getDimensionPixelSize(R.dimen.category_grid_item_file_info_temp_margin_top), 0, 0);
            textView.setLayoutParams(lp);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, res.getDimension(R.dimen.category_grid_item_file_info_first_line_text_size));
            sizeView.setLayoutParams(lp);
            sizeView.setTextSize(TypedValue.COMPLEX_UNIT_PX, res.getDimension(R.dimen.category_grid_item_file_info_second_line_text_size));

            if (!mSetDetailWeight.get(mDepth)) {
                int heightName = getHeightTextView(textView);
                int heightSize = getHeightTextView(sizeView);
                setDetailWeight(heightName, heightSize);
                mSetDetailWeight.put(mDepth, true);
            }
        }

    }

    private void setDetailWeight(int heightName, int heightSize) {
        int marginTop = mContext.getResources().getDimensionPixelSize(R.dimen.category_grid_item_file_info_temp_margin_top);
        int paddingLayout = 2 * mContext.getResources().getDimensionPixelSize(R.dimen.grid_item_detail_info_padding_vertical);

        int adding = 0;
        int basicEx = (paddingLayout + heightName + marginTop);
        switch (mDepth) {
            case 2:
                break;
            case 1:
                adding = heightSize + marginTop;
                break;
            case 0:
                adding = heightSize + marginTop + heightSize;
                break;
        }
        mDetailWeight[mDepth] = 1000 * (basicEx + adding) / mHeight[mDepth];

    }

    private int getHeightTextView(TextView textView) {
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        Point size = new Point();
        wm.getDefaultDisplay().getSize(size);
        int screenWidth = size.x;
        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(screenWidth, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        textView.measure(widthMeasureSpec, heightMeasureSpec);
        return textView.getMeasuredHeight();
    }

    private void setWeightLayout(RelativeLayout layoutThumbnail, RelativeLayout layoutDetail, float weightThumbnail, float weightFileDetail) {
        if (layoutDetail != null && layoutThumbnail != null) {
            layoutThumbnail.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, weightThumbnail));
            layoutDetail.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, weightFileDetail));
        }
    }

    private void setScaleViewSize(View v) {
        ViewGroup.LayoutParams params = v.getLayoutParams();
        if (UiUtils.isCategoryFolderList(mCurRecord)) {
            params.height = mWidth[mDepth] + mContext.getResources().getDimensionPixelSize(R.dimen.category_grid_album_item_text_view_height);
        } else {
            if (mDepth <= 1) {
                params.height = mWidth[mDepth] + 24;
            } else {
                params.height = mWidth[mDepth];
            }
        }
        params.width = mWidth[mDepth];
        mHeight[mDepth] = params.height;

        v.setLayoutParams(params);
    }

    private void setFileTypeIconSize(ListViewHolder vh) {
        View fileTypeIcon = vh.getView(FileListViewHolder.FILE_TYPE_ICON, ImageView.class);
        if (fileTypeIcon != null) {
            ViewGroup.LayoutParams params = fileTypeIcon.getLayoutParams();
            if (params != null) {
                params.width = mFileTypeIconWidth[mDepth];
                params.height = mFileTypeIconHeight[mDepth];
                fileTypeIcon.setLayoutParams(params);
            }
        }
    }

    private void setAlignment(ListViewHolder vh, int viewKey, int alignment) {
        TextView v = (TextView) vh.getView(viewKey);
        if (v != null) {
            v.setTextAlignment(alignment);
        }
    }

    private boolean setImageInfo(FileRecord record, Cursor cursor) {
        final int widthIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.WIDTH);
        final int heightIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.HEIGHT);

        boolean bRet = false;
        String resolution = null;
        if (!cursor.isNull(widthIndex) && !cursor.isNull(heightIndex)) {
            int width = cursor.getInt(widthIndex);
            int height = cursor.getInt(heightIndex);

            if (width > 0 && height > 0) {
                resolution = String.format("%dx%d", width, height);
            }
            bRet = true;
        }
        Object info[] = new Object[]{
                resolution
        };

        record.setDetailMediaInfo(info);
        return bRet;
    }

    private boolean setVideoInfo(FileRecord record, Cursor cursor) {
        final int durationIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.DURATION);
        final int resumePosIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.RESUME_POS);
        boolean bRet = false;

        if (!cursor.isNull(durationIndex)) {
            Long duration = cursor.getLong(durationIndex);
            Long resumePos = null;
            if (resumePosIndex >= 0) {
                resumePos = cursor.getLong(resumePosIndex);
            }

            Object info[] = {
                    duration,
                    resumePos
            };

            record.setDetailMediaInfo(info);
            bRet = true;
        }
        return bRet;
    }


    private boolean setAudioInfo(FileRecord record, Cursor cursor) {
        final int artistIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.ARTIST);
        final int albumIndex = mTableInfo.getIndex(DbTableInfo.COLUMN_ID.ALBUM);

        boolean bRet = false;

        String artist = null;
        String album = null;
        if (!cursor.isNull(artistIndex) && !cursor.isNull(albumIndex)) {
            artist = cursor.getString(artistIndex);
            album = cursor.getString(albumIndex);

            bRet = true;
        }

        Object info[] = {
                artist,
                album
        };
        record.setDetailMediaInfo(info);

        return bRet;
    }

    private static boolean sIsPinchWorking;
    private static SparseArray<View> sScrapPinchViewList;

    public static boolean getIsPinchWorking() {
        return sIsPinchWorking;
    }

    public static synchronized void setIsPinchWorking() {
        if (sScrapPinchViewList == null) {
            sScrapPinchViewList = new SparseArray<>();
        }
        sScrapPinchViewList.clear();

        sIsPinchWorking = true;
    }

    public static synchronized void unsetIsPinchWorking() {
        sIsPinchWorking = false;
        if (sScrapPinchViewList != null) {
            sScrapPinchViewList.clear();
            sScrapPinchViewList = null;
        }
    }

    @Override
    protected View _getView(int position, View convertView, ViewGroup parent) {
        View ret = null;
        if (sIsPinchWorking) {
            ret = sScrapPinchViewList.get(position);
            if (ret == null) {
                ret = sScrapPinchViewList.get(0);
            }
        }
        return ret;
    }

    @Override
    protected void _scrapView(int position, View view) {
        if (sIsPinchWorking) {
            sScrapPinchViewList.put(position, view);
        }
    }

    public void updateItemWidth(Context context) {
        Resources res = context.getResources();
        mWidth = new int[]{
                (int) res.getDimension(R.dimen.category_grid_two_item_size),
                (int) res.getDimension(R.dimen.category_grid_three_item_size),
                (int) res.getDimension(R.dimen.list_item_grid_size)
        };
        mSetDetailWeight.clear();
    }
}